﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GenericGA.reader.tree
{
    /// <summary>
    /// A template which describes a node in a tree out of which an instance of the actual node will be created.
    /// </summary>
    /// <typeparam name="N">The base class type of all the actual nodes in the tree.</typeparam>
    public class NodeTemplate<N> where N : class
    {

        /// <summary>
        /// The node's label to be returned by ToString.
        /// </summary>
        public string Label { get; private set; }

        /// <summary>
        /// The template type of the node which restricts which nodes it can be a child of.
        /// </summary>
        public string[] NodeTypes
        {
            get
            {
                return nodeTypes;
            }
        }
        private string[] nodeTypes;

        /// <summary>
        /// The template types of the children which can be connected to this node.
        /// </summary>
        public string[] ChildTypes
        {
            get
            {
                return childTypes;
            }
        }
        private string[] childTypes;
        
        /// <summary>
        /// The node factory which returns an instance of the actual node out it's children.
        /// </summary>
        private TreeTemplate<N>.NodeMaker nodeMaker;

        /// <summary>
        /// The default children of the node if any are missing.
        /// </summary>
        private List<N> defaultChildren;

        /// <summary>
        /// Create a node template.
        /// </summary>
        /// <param name="label">The node's label to be returned by ToString.</param>
        /// <param name="nodeType">The template types of the node which restricts which nodes it can be a child of.</param>
        /// <param name="childTypes">The template types of the children which can be connected to this node.</param>
        /// <param name="defaultChildren">The default children of the node if any are missing. These are resorted to when the maximum number of nodes to use are exceeded. These nodes should either be temselves terminals or they should be roots of trees where all branches lead to terminals.</param>
        /// <param name="nodeMaker">The node factory which returns an instance of the actual node out it's children.</param>
        public NodeTemplate(string label, string[] nodeTypes, string[] childTypes, List<N> defaultChildren, TreeTemplate<N>.NodeMaker nodeMaker)
        {
            if (childTypes.Length != defaultChildren.Count)
            {
                throw new ArgumentException();
            }
            if (nodeTypes.Length == 0)
            {
                throw new ArgumentException();
            }

            this.Label = label;
            this.nodeTypes = nodeTypes;
            this.childTypes = childTypes;
            this.nodeMaker = nodeMaker;
            this.defaultChildren = defaultChildren;
        }

        /// <summary>
        /// Create a node template.
        /// </summary>
        /// <param name="label">The node's label to be returned by ToString.</param>
        /// <param name="nodeType">The template type of the node which restricts which nodes it can be a child of.</param>
        /// <param name="childTypes">The template types of the children which can be connected to this node.</param>
        /// <param name="defaultChildren">The default children of the node if any are missing. These are resorted to when the maximum number of nodes to use are exceeded. These nodes should either be temselves terminals or they should be roots of trees where all branches lead to terminals.</param>
        /// <param name="nodeMaker">The node factory which returns an instance of the actual node out it's children.</param>
        public NodeTemplate(string label, string[] nodeTypes, string[] childTypes, N[] defaultChildren, TreeTemplate<N>.NodeMaker nodeMaker)
            : this(label, nodeTypes, childTypes, new List<N>(defaultChildren), nodeMaker)
        {
        }

        /// <summary>
        /// Create an actual node out of it's children, replacing any missing children (nulls) with their respective default children.
        /// </summary>
        /// <param name="children">The child nodes.</param>
        /// <param name="reader">A reader object.</param>
        /// <returns>An actual node.</returns>
        public N CreateNode(List<N> children, Reader reader)
        {
            if (children.Count != defaultChildren.Count)
            {
                throw new ArgumentException();
            }

            for (int i = 0; i < children.Count; i++)
            {
                if (children[i] == null)
                {
                    children[i] = defaultChildren[i];
                }
            }
            return nodeMaker(children, reader);
        }

        public override string ToString()
        {
            return Label;
        }

    }
}
